import DesignComponents from '@/components/form/DesignComponents';
import {ElMessage, ElMessageBox} from 'element-plus';
import DavidTable from '@/components/form/table/DavidTable';
import {guid, isEmpty} from '@/utils/CommonUtils';
import TableComponentConfig from '@/components/form/table/TableComponentConfig';
import SubTableComponentConfig from '@/components/form/SubTable/SubTableComponentConfig.vue';

class DraggerDesigner {
  constructor(rootId, configs) {
    this.rootId = rootId;
    this.dragElement = null;
    this.placeholder = null;
    this.draggHoverBlock = null;
    this.selectingComponent = null;

    this.configs = configs;
    this.resolveDefaultConfigs();
  }

  resolveDefaultConfigs() {
    if (!this.configs) this.configs = {};
    let defaultConfigs = this.getDefaultConfigs();
    this.mereConfigs(defaultConfigs);
  }

  bind() {
    $(`#${this.rootId} [data-role='icon']`).attr('draggable', 'true');
    $(`#${this.rootId} [data-role='block']`).attr('draggable', 'true');

    $(`#${this.rootId} [data-role='icon']`).off('dragstart').on('dragstart', e => this.dragStart(e));
    $(`#${this.rootId} [data-role='block']`).off('dragstart').on('dragstart', e => this.dragStart(e));

    $(`#${this.rootId} [data-role='field']`).off('dragstart').on('dragstart', e => this.dragStart(e));
    $(`#${this.rootId} [data-role='subFormField']`).off('dragstart').on('dragstart', e => this.dragStart(e));

    $(`#${this.rootId} .drag-delete-bar`).off('click').on('click', e => this.removeComponent(e));
    $(`#${this.rootId} .drag-selectable`).off('click').on('click', e => this.selectComponent(e));
  }

  dragStart(e) {
    let that = this, target = e.target;
    this.dragElement = target;

    if (this.isBlock(target)) {
      this.setDragingBlock(target);
    }

    $(`#${this.rootId} [data-role='container']`).off('dragenter').on('dragenter', e => this.containerDragenter(e));
    $(`#${this.rootId} [data-role='block-container']`).off('dragenter').on('dragenter', e => this.containerDragenter(e));

    $(`#${this.rootId} [data-role='container']`).off('dragleave').on('dragleave', e => this.containerDragLeave(e));
    $(`#${this.rootId} [data-role='container']`).off('dragover').on('dragover', e => this.containerDragover(e));

    function fun(e) {
      that.clearDraggHoverBlockContainer();
      that.replacePlaceholder(() => {
        that.clearDraggingData(e.target);
        that.bind();
        $(target).unbind('dragend', fun);
        that.invokeViewChange();
      });
    }

    $(target).on('dragend', fun);

    $(`#${this.rootId}`).parent().off('dragenter').on('dragenter', e => {
      this.clearPlaceholder();
    });
  }

  clearContainer() {
    $(`#${this.rootId} [data-role='container']`).html('');
  }

  getContentData() {
    return $(`#${this.rootId} [data-role='container']`).html();
  }

  setViewData({viewDesignDefinition}) {
    this.clearContainer();
    $(`#${this.rootId} [data-role='container']`).html(viewDesignDefinition);
    this.bind();
    this.bindComponents();
  }

  bindComponents() {
    let components = $(`#${this.rootId} [data-component]`);
    for (let i = 0; i < components.length; i++) {
      let componentEle = components[i];
      let compName = this.getComponentName(componentEle);
      let component = DesignComponents.getComponent(compName);

      if (this.isIcon(componentEle)) {
      } else {
        component.bindWithElement(componentEle, {
          onViewChange: this.configs.onViewChange
        });
      }
    }
  }

  bindComponent(component, dragElement, componentConfig) {
    component.bind(dragElement, {
      onViewChange: this.configs.onViewChange,
      ...componentConfig
    });
  }

  bindFieldComponent(component, dragElement) {
    component.bind(dragElement, {
      onViewChange: this.configs.onViewChange
    });
  }

  bindWithElement(component, componentEle) {
    component.bindWithElement(componentEle, {
      onViewChange: this.configs.onViewChange
    });
  }

  clearDraggingData(ele) {
    this.removeDragingBlock(ele);
    this.clearPlaceholder();
    $(`#${this.rootId} .dragging-block`).removeClass('dragging-block');
  }

  invokeViewChange() {
    this.configs.onViewChange();
  }

  invokeGetFormDefinitionViewChange() {
    return this.configs.getFormDefinition();
  }

  removeComponent(e) {
    let comp = this.getDragOverElement(e.target);
    ElMessageBox.confirm('删除后不可恢复, 确定删除组件吗', '确定删除?', {
      draggable: true,
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    }).then(() => {
      $(comp).remove();
      this.invokeViewChange();
    }).catch(() => {
    });
  }

  containerDragenter(e) {
    this.prevent(e);
    let target = e.target;
    if (this.isIgnoreDragElement(target)) {
      return;
    }

    if (this.isPlaceholder(target)) {
      return;
    }
    target = this.getDragOverElement(target);
    if (this.isRootContainer(target)) {
      this.placeholder = this.getAndCreatePlaceholder();
      target.append(this.placeholder);
    }
    if (this.isBlock(target)) {
      if (this.dragEnterDuplicate(target)) return;
      if (this.isBlockContainer(target.parentNode) && this.isDraggingLayout()) return;
      if (this.isSingleChildContainer(target.parentNode)) return;
      this.placeholder = this.getAndCreatePlaceholder();
      $(target).before(this.placeholder);
    }
    if (this.isBlockContainer(target)) {
      if (this.isSingleChildContainer(target) && this.hasChildren(target)) return;
      this.draggHoverBlockContainer(target);
      if (this.isDraggingLayout()) return;
      this.placeholder = this.getAndCreatePlaceholder();
      target.append(this.placeholder);
    }
  }

  containerDragLeave(e) {
  }

  containerDragover(e) {
    this.prevent(e);
  }

  clearPlaceholder() {
    this.removePlaceholderFromParent();
    this.placeholder = null;
  }

  replacePlaceholder(callback) {
    let cancelCallback = callback;
    if (!this.placeholder) {
      callback();
      return;
    }
    let fieldId = this.createFieldId();
    if (this.isSubTable(this.dragElement) && this.isBlock(this.dragElement)) {

    } else if (this.exitsFeild(fieldId)) {
      ElMessage({message: '这个字段或明细表在当前视图已存在', type: 'warning'});
      callback();
      return;
    }

    console.log('fieldId:', fieldId);
    if (this.isIcon(this.dragElement)) {
      this.createComponentConfig(componentConfig => {
        this.doReplacePlaceholder(fieldId, componentConfig);
        callback();
      }, cancelCallback);
    } else {
      this.doReplacePlaceholder(fieldId);
      callback();
    }
  }

  createComponentConfig(callback, cancelCallback) {
    let compName = this.getComponentName(this.dragElement);
    if (compName === 'table') {
      this.createTableComponentConfig(callback, cancelCallback);
      return;
    }
    if (compName === 'SubTable') {
      this.createSubTableComponentConfig(callback, cancelCallback);
      return;
    }
    callback();
  }

  createSubTableComponentConfig(callback, cancelCallback) {
    let configComponent = <SubTableComponentConfig/>;
    ElMessageBox({
      title: '明细表属性',
      message: () => configComponent,
    }).then(() => {
      callback(configComponent.component.ctx.getData());
    }).catch(() => {
      cancelCallback();
    });
  }

  createTableComponentConfig(callback, cancelCallback) {
    let configComponent = <TableComponentConfig/>;
    ElMessageBox({
      title: '表格属性',
      message: () => configComponent,
    }).then(() => {
      callback(configComponent.component.ctx.getData());
    }).catch(() => {
      cancelCallback();
    });
  }

  doReplacePlaceholder(fieldId, componentConfig) {
    let dragElement = this.getDraggingElement(fieldId);
    let fieldConfig = this.createFieldConfig(fieldId);

    //增加字段
    if (!this.createField(fieldConfig)) return;
    //替换placeholder
    this.replacePlaceholderElement(dragElement);

    let compName = this.getComponentName(this.dragElement);
    let component = DesignComponents.getComponent(compName);
    if (this.isIcon(this.dragElement)) {
      this.bindComponent(component, dragElement, componentConfig);
    } else if (this.isField(this.dragElement)) {
      this.bindComponent(component, dragElement);
    } else if (this.isSubTableField(this.dragElement)) {
      this.bindSunTableComponent(component, dragElement);
    } else {
      this.bindWithElement(component, dragElement);
    }
  }

  replacePlaceholderElement(dragElement) {
    $(dragElement).remove();
    if (this.isSingleChildContainer(this.placeholder.parentNode)) {
      this.pushSingleChildContainer(dragElement, this.placeholder.parentNode);
    } else {
      this.placeholder.replaceWith(dragElement);
    }
  }

  createField(fieldConfig) {
    if (this.isSubTable(this.dragElement)) {
      this.invokeFieldCreated(fieldConfig, 'subForm');
    } else if (this.isIcon(this.dragElement)) {//当前拖拽的是图标
      //放在主表里面, 或者放在明细表里面
      if (this.isPlaceholderInSumTable()) {
        let subFormId = this.getPloceholderSubFormId();
        this.invokeFieldCreated(fieldConfig, 'subFormItem', subFormId);
      } else {
        this.invokeFieldCreated(fieldConfig, 'main');
      }
    } else if (this.isField(this.dragElement)) { // 当前拖拽的是已经定义的字段
      //放在主表里面, 或者放在明细表里面
      return this.canDrag()
    } else if (this.isBlock(this.dragElement)) {
      return this.canDrag()
    }
    return true;
  }

  canDrag() {
    let fieldId = this.getFieldComponentId(this.dragElement);
    if(isEmpty(fieldId)) fieldId = this.getFieldId();

    // 主表往明细表里面拖
    if (this.isMainFormField(fieldId) && this.isPlaceholderInSumTable()) {
      ElMessage.warning('主表字段不能放置在明细表字段区域');
      return false;
    }

    //明细表往主表里面拖
    if (this.isSubFormField(fieldId) && !this.isPlaceholderInSumTable()) {
      ElMessage.warning('明细表字段不能放置在主表字段区域');
      return false;
    }

    //明细表往不属于自己的明细表里面拖
    if (this.isSubFormField(fieldId) && this.isPlaceholderInSumTable()) {
      let subFormId = this.getPloceholderSubFormId();
      let subFormId1 = this.getSubFormIdByFieldId(fieldId);
      if (subFormId !== subFormId1) {
        ElMessage.warning('不能把不同明细表字段放置在一起');
        return false;
      }
    }
    return true
  }

  getFieldComponentId(dragElement) {
    return $(dragElement).attr('id');
  }

  getSubFormIdByFieldId(fieldId) {
    let formDefinition = this.invokeGetFormDefinitionViewChange();
    if (isEmpty(formDefinition)) formDefinition = {};
    let subForms = formDefinition.subForms;
    if (isEmpty(subForms)) subForms = [];
    for (let i = 0; i < subForms.length; i++) {
      let fields = subForms[i].fields;
      if (isEmpty(fields)) fields = [];
      if (fields.findIndex(f => f.id === fieldId) !== -1) return subForms[i].id;
    }
    return false;
  }

  isSubFormField(fieldId) {
    let formDefinition = this.invokeGetFormDefinitionViewChange();
    if (isEmpty(formDefinition)) formDefinition = {};
    let subForms = formDefinition.subForms;
    if (isEmpty(subForms)) subForms = [];
    for (let i = 0; i < subForms.length; i++) {
      let fields = subForms[i].fields;
      if (isEmpty(fields)) fields = [];
      if (fields.findIndex(f => f.id === fieldId) !== -1) return true;
    }
    return false;
  }

  isMainFormField(fieldId) {
    let formDefinition = this.invokeGetFormDefinitionViewChange();
    if (!formDefinition) formDefinition = {};
    let fields = formDefinition.fields;
    return fields.findIndex(f => f.id === fieldId) !== -1;
  }

  getFieldId() {
    return $(this.dragElement).attr('data-field-id');
  }

  getPloceholderSubFormId() {
    let ele = this.placeholder;
    while (ele && !this.isSubTable(ele)) {
      ele = ele.parentNode;
    }
    if (ele) {
      return $(ele).attr('id');
    }
    throw new Error('没有找到正在退拽的明细表的id!');
  }

  isPlaceholderInSumTable() {
    let ele = this.placeholder;
    while (ele && !this.isSubtableDetail(ele)) {
      ele = ele.parentNode;
    }
    return !!ele;
  }

  isSubtableDetail(ele) {
    return this.getRole(ele) === 'subtable-detail';
  }

  getRole(ele) {
    if (!ele) return null;
    return $(ele).attr('data-role');
  }

  bindSunTableComponent(subTableComponent, ele) {
    let subFormDefinition = this.invokeGetSubFormDefinition($(ele).attr(`id`));
    if (isEmpty(subFormDefinition)) return;
    subTableComponent.bindWidthFormDefinition(ele, subFormDefinition);
    this.invokeViewChange();
  }

  invokeGetSubFormDefinition(subFormId) {
    return this.configs.getSubFormDefinition(subFormId);
  }

  invokeFieldCreated(fieldConfig, formName, subFormId) {
    this.configs.fieldCreated(fieldConfig, formName, subFormId);
  }

  getAndCreatePlaceholder() {
    if (!this.placeholder) {
      this.placeholder = $(`<div class="placeholder">放在这里</div>`)[0];
    } else {
      $(this.placeholder).remove();
    }

    return this.placeholder;
  }

  prevent(e) {
    e.preventDefault();
    e.stopPropagation();
  }

  isRootContainer(ele) {
    return $(ele).attr('data-role') === 'container';
  }

  removePlaceholderFromParent() {
    if (this.placeholder) {
      $(this.placeholder).remove();
    }
  }

  createFieldId() {
    if (this.isField(this.dragElement)) {
      return $(this.dragElement).attr('data-field-id');
    }
    if (this.isSubTableField(this.dragElement)) {
      return $(this.dragElement).attr('data-sub-form-id');
    }
    if (this.isBlock(this.dragElement) && this.isSubTable(this.dragElement)) {
      return $(this.dragElement).attr('id');
    }
    return guid();
  }

  getDraggingElement(fieldId) {
    if (this.isIcon(this.dragElement)
      || this.isField(this.dragElement)
      || this.isSubTableField(this.dragElement)) {
      let compName = this.getComponentName(this.dragElement);
      return this.getElementByIcon(compName, fieldId);
    }
    return this.dragElement;
  }

  createFieldConfig(fieldId) {
    if (this.isIcon(this.dragElement)
      || this.isSubTable(this.dragElement)) {
      let compName = this.getComponentName(this.dragElement);
      return this.createFieldConfigByIcon(compName, fieldId);
    }
    return null;
  }

  isIcon(ele) {
    return $(ele).attr('data-role') === 'icon';
  }

  isSubTable(ele) {
    return this.getComponentName(ele) === 'SubTable';
  }

  isField(ele) {
    return $(ele).attr('data-role') === 'field';
  }

  isSubTableField(ele) {
    return $(ele).attr('data-role') === 'subFormField';
  }

  getComponentName(ele) {
    return $(ele).attr('data-component');
  }

  getElementByIcon(compName, fieldId) {
    let component = DesignComponents.getComponent(compName);
    if (component) {
      return component.getBody(fieldId);
    }
  }

  createFieldConfigByIcon(compName, fieldId) {
    let component = DesignComponents.getComponent(compName);
    if (component && component.createFieldConfig) {
      return component.createFieldConfig(fieldId);
    }
    return null;
  }

  isBlock(ele) {
    let dataRole = $(ele).attr('data-role');
    return dataRole === 'block' || dataRole === 'field';
  }

  isPlaceholder(ele) {
    return $(ele).hasClass('placeholder');
  }

  getDragOverElement(ele) {
    let temp = ele;
    while (temp) {
      if (this.isContainer(temp) || this.isBlock(temp)) {
        return temp;
      }
      temp = temp.parentNode;
    }
    return ele;
  }

  isContainer(ele) {
    let role = $(ele).attr('data-role');
    return role === 'container' || role === 'block-container';
  }

  setDragingBlock(ele) {
    $(ele).addClass('dragging-block');
  }

  removeDragingBlock(ele) {
    $(ele).removeClass('dragging-block');
  }

  isBlockContainer(ele) {
    let role = $(ele).attr('data-role');
    return role === 'block-container';
  }

  exitsFeild(fieldId) {
    let ele = $(`#${fieldId}`);
    ele = ele[0];
    return !!ele;
  }

  isDraggingLayout() {
    if (!this.dragElement) return false;
    return $(this.dragElement).attr('data-is-layout') === 'true';
  }

  isIgnoreDragElement(ele) {
    return $(ele).hasClass('ignore-drag');
  }

  dragEnterDuplicate(ele) {
    ele = this.getDragOverElement(ele);
    if (ele === this.dragElement) return true;
    if (this.dragElement === $(ele).prev()[0]) return true;
    if (this.placeholder && $(this.placeholder).next()[0] === this.dragElement) return true;
    if (this.placeholder && $(this.dragElement).next()[0] === this.placeholder) return true;
    return false;
  }

  draggHoverBlockContainer(blockContainer) {
    let block = this.getBlockParent(blockContainer);
    if (!block) return;
    if (this.draggHoverBlock === block) {
    } else {
      this.setDraggHoverBlockContainer(block);
    }
  }

  getBlockParent(blockContainer) {
    blockContainer = blockContainer.parentNode;
    while (blockContainer && !this.isBlock(blockContainer)) {
      blockContainer = blockContainer.parentNode;
    }
    return blockContainer;
  }

  clearDraggHoverBlockContainer() {
    if (this.draggHoverBlock) {
      $(this.draggHoverBlock).removeClass('dragging-hover-layout-component');
      this.draggHoverBlock = null;
    }
  }

  setDraggHoverBlockContainer(block) {
    this.clearDraggHoverBlockContainer();
    this.draggHoverBlock = block;
    $(this.draggHoverBlock).addClass('dragging-hover-layout-component');
  }

  isSingleChildContainer(ele) {
    return $(ele).attr('data-single-child') === 'true';
  }

  pushSingleChildContainer(dragElement, parent) {
    $(parent).html('');
    parent.append(dragElement);
  }

  setContainerWidth(width) {
    $(`#${this.rootId} [data-role='container']`).css('width', `${width}px`);
    let tables = $(`#${this.rootId} .david-table`);
    for (let i = 0; i < tables.length; i++) {
      let id = $(tables[i]).attr('id');
      let table = new DavidTable(id, {onlyBind: true});
      table.refreshWidthKeepPercent();
    }
  }

  hasChildren(ele) {
    let count = 0, children = $(ele).children();
    for (let i = 0; i < children.length; i++) {
      if (children[i].tagName === 'br' || children[i].tagName === 'BR') {

      } else {
        count++;
      }
    }
    return !!count;
  }

  selectComponent(e) {
    let target = e.target;
    target = this.getSelectableParent(target);
    if (!target) return;
    this.setSelectingComponent(target);
    this.prevent(e);
    this.setSelectingStyle();

    let that = this;

    function fun(e) {
      let ele = e.target;
      if (that.hasIgnoreClickParent(ele)) return;
      that.clearSelectingData();
      $(document).unbind('click', fun);
      that.invokeViewChange();
    }

    $(document).on('click', fun);
  }

  hasIgnoreClickParent(ele) {
    while (ele && !$(ele).hasClass('item-ignore-click')) ele = ele.parentNode;
    return !!ele;
  }

  clearSelectingData() {
    this.removeSelectingStyle();
    this.selectingComponent = null;
    this.invokeClearSelectedComponent();
  }

  setSelectingStyle() {
    $(this.selectingComponent).addClass('selected-component');
  }

  setSelectingComponentId(id) {
    let ele = $(`#${id}`);
    if (ele && ele.length >= 1) {
      this.clearSelectingData();
      this.selectingComponent = ele[0];

      this.selectComponent({
        target: this.selectingComponent,
        preventDefault: () => {
        },
        stopPropagation: () => {
        },
      });
    }
  }

  setSelectingComponent(ele) {
    this.clearSelectingData();
    this.selectingComponent = ele;

    this.invokeSeleted();
  }

  invokeSeleted() {
    this.configs.selectedComponent(this.selectingComponent);
  }

  removeSelectingStyle() {
    $(this.selectingComponent).removeClass('selected-component');
  }

  isSelectable(ele) {
    return $(ele).hasClass('drag-selectable');
  }

  getSelectableParent(ele) {
    while (ele && !this.isSelectable(ele)) ele = ele.parentNode;
    return ele;
  }

  mereConfigs(defaultConfigs) {
    for (let key in defaultConfigs) {
      if (!this.configs[key]) {
        this.configs[key] = defaultConfigs;
      }
    }
  }

  getDefaultConfigs() {
    return {
      selectedComponent() {
      },
      clearSelectedComponent() {
      },
      fieldCreated(component, formName, subFormId) {
      },
      onViewChange() {
      },
      getSubFormDefinition(subFormId) {
      },
      getFormDefinition() {
      },
    };
  }

  invokeClearSelectedComponent() {
    this.configs.clearSelectedComponent();
  }
}

export default DraggerDesigner;
